﻿//------------------------------------------------------------------------------
// <copyright file="Hooks.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Internal.GamesTest.Rpc.Server;
using Windows.UI.Core;

namespace Rpc.CSharpSample
{
    /// <summary>
    /// General test hooks.
    /// </summary>
    internal static class Hooks
    {
        private static RpcServer server;
        private static RpcServer server2;
        private static RpcServer server3;
        private static RpcServer serverWithQueueSize;
        private static short testHookOrderCounter;

        /// <summary>
        /// Initialize the RPC Server.
        /// </summary>
        /// <param name="port">Port to initialize on.</param>
        public static void Initialize(int port)
        {
            server = new RpcServer(RpcServer.DefaultServerId, port == 0 ? RpcServer.DefaultPort : port);

            server.RegisterHookMethod("OutputNoInput", OutputNoInput);
            server.RegisterHookMethod("InputNoOutput", InputNoOutput);
            server.RegisterHookMethod("InputOutput", InputOutput);
            server.RegisterHookMethod("NoInputNoOutput", NoInputNoOutput);
            server.RegisterHookMethod("MultiInputMultiOutput", MultiInputMultiOutput);
            server.RegisterHookMethod("HookTimesOut", HookTimesOut);
            server.RegisterHookMethod("HookWaitTenSeconds", HookWaitTenSeconds);
            server.RegisterHookMethod("ReturnLargeNumberOfValues", ReturnLargeNumberOfValues);
            server.RegisterHookMethod("ReturnErrorCode", ReturnErrorCode);
            server.RegisterHookMethod("SimultaneousHookCalls", SimultaneousHookCalls);
            server.RegisterHookMethod("LargeNumberOfInputValues", LargeNumberOfInputValues);
            server.RegisterHookMethod("EchoStrings", EchoStrings);
            server.RegisterHookMethod("RetrieveMissingRpcArchiveField", RetrieveMissingRpcArchiveField);
            server.RegisterHookMethod("RpcArchiveValueIsNull", RpcArchiveValueIsNull);
            server.RegisterHookMethod("CallProcessPendingRequestsOnceOnServer3", CallProcessPendingRequestsOnceOnServer3);
            server.RegisterHookMethod("InputOutputIntCollection", InputOutputIntCollection);
            server.RegisterHookMethod("InputOutputInt64Collection", InputOutputInt64Collection);
            server.RegisterHookMethod("InputOutputFloatCollection", InputOutputFloatCollection);
            server.RegisterHookMethod("InputOutputStringCollection", InputOutputStringCollection);
            server.RegisterHookMethod("InputOutputIntCollectionAndIntNumber", InputOutputIntCollectionAndIntNumber);
            server.RegisterHookMethod("InputOutputMultipleCollections", InputOutputMultipleCollections);
            server.RegisterHookMethod("TriggerEvent", TriggerEvent);
            server.RegisterHookMethod("AddArchiveToArchive", AddArchiveToArchive);
            server.RegisterHookMethod("AddArchiveToArchiveWithIntColl", AddArchiveToArchiveWithIntColl);
            server.RegisterHookMethod("AddArchiveToArchiveWithStringColl", AddArchiveToArchiveWithStringColl);
            server.RegisterHookMethod("AddArchiveToArchiveWithInt64Coll", AddArchiveToArchiveWithInt64Coll);
            server.RegisterHookMethod("InputOutputArchiveCollectionToArchive", InputOutputArchiveCollectionToArchive);
            server.RegisterHookMethod("InputOutputArchiveCollectionToArchiveCollections", InputOutputArchiveCollectionToArchiveCollections);
            server.RegisterHookMethod("InputRpcArchiveToAnotherRpcArchiveForLoad", InputRpcArchiveToAnotherRpcArchiveForLoad);
            server.RegisterHookMethod("InputRpcArchiveToAnotherRpcArchiveCollectionForLoad", InputRpcArchiveToAnotherRpcArchiveCollectionForLoad);
            server.RegisterHookMethod("InputPlayerNoOutput", UserDefinedTypeHooks.InputPlayerNoOutput);
            server.RegisterHookMethod("NoInputPlayerOutput", UserDefinedTypeHooks.NoInputPlayerOutput);
            server.RegisterHookMethod("InputPlayerOutputPlayer", UserDefinedTypeHooks.InputPlayerOutputPlayer);
            server.RegisterHookMethod("ThrowException", ThrowException);
            server.RegisterHookMethod("EchoBinary", EchoBinary);
            server.RegisterHookMethod("AddBinaryDataToArchive", AddBinaryDataToArchive);
            server.RegisterHookMethod("AddBinaryDataCollectionToArchive", AddBinaryDataCollectionToArchive);
            server.RegisterHookMethod("NoInputBinaryDataOutput", NoInputBinaryDataOutput);
            server.RegisterHookMethod("StartServerWithQueueSize", StartServerWithQueueSize);
            server.RegisterHookMethod("StopServerWithQueueSize", StopServerWithQueueSize);
            server.RegisterHookMethod("RunServerWithQueueSize", RunServerWithQueueSize);

            server.Start();

            // Warning: If you modify this guid then you need to modify the same guid value in Rpc.Tests.XboxEndToEndTests.cs file.
            Guid guid = Guid.Parse("4FC5C96E-2ECD-4A45-BF88-59EFFC184682");

            server2 = new RpcServer(guid);
            server2.RegisterHookMethod("InputOutputSubtraction", InputOutputSubtraction);
            server2.RegisterHookMethod("StopTheServer", StopTheServer);
            server2.RegisterHookMethod("CallProcessPendingRequestsOnceOnServer3", CallProcessPendingRequestsOnceOnServer3);
            server2.Start();

            Guid guid3 = Guid.Parse("48810C25-BC83-4934-9085-8B00275FD4F5");
            server3 = new RpcServer(guid3);
            server3.RegisterHookMethod("InputOutput", InputOutput);
            server3.RegisterHookMethod("InputOutputImmediate", InputOutput, true);
            server3.RegisterHookMethod("InputNoOutput", InputNoOutput);
            server3.RegisterHookMethod("OutputNoInput", OutputNoInput);
            server3.RegisterHookMethod("MultiInputMultiOutput", MultiInputMultiOutput);
            server3.RegisterHookMethod("HookOrderIncrementNoOutput", HookOrderIncrementNoOutput);
            server3.RegisterHookMethod("GetHookOrderCounter", GetHookOrderCounter);
            server3.RegisterHookMethod("TriggerEvent", TriggerEvent);
            server3.Start();
        }

        /// <summary>
        /// Performs shutdown of all started RPC servers.
        /// </summary>
        public static void Shutdown()
        {
            try
            {
                server.Stop();
            }
            catch (Exception error)
            {
                System.Diagnostics.Debug.WriteLine(error.ToString());
            }

            try
            {
                server2.Stop();
            }
            catch (Exception error)
            {
                System.Diagnostics.Debug.WriteLine(error.ToString());
            }

            try
            {
                server3.Stop();
            }
            catch (Exception error)
            {
                System.Diagnostics.Debug.WriteLine(error.ToString());
            }
        }

        /// <summary>
        /// Run the RPC Server.
        /// </summary>
        public static void ProcessPendingRequests()
        {
            try
            {
                if (server != null)
                {
                    server.ProcessPendingRequests();
                }

                if (server2 != null)
                {
                    server2.ProcessPendingRequests();
                }
            }
            catch (Exception error)
            {
                System.Diagnostics.Debug.WriteLine(error.ToString());
            }
        }

        private static int TriggerEvent(RpcArchive eventName, RpcArchive eventParameter)
        {
            try
            {
                string eventNameValue = eventName.GetNamedStringValue("eventName");

                eventParameter.SetNamedStringValue("result", "Test Event Parameter");

                // This needs to be implemented
                server.RaiseEvent(eventNameValue, eventParameter);

                // Raise event for server for test case 12
                // If required we can optionally send server name as parameter in RpcArchive object to avoid both raise events to be called
                server3.RaiseEvent(eventNameValue, eventParameter);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int OutputNoInput(RpcArchive input, RpcArchive output)
        {
            try
            {
                output.SetNamedIntValue("result", 3871568);
                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int EchoStrings(RpcArchive input, RpcArchive output)
        {
            try
            {
                int numStrings = input.GetNamedIntValue("numStrings");
                string[] stringArray = new string[numStrings];

                for (int i = 0; i < numStrings; ++i)
                {
                    string fieldName = string.Format(CultureInfo.CurrentCulture, "input{0}", i.ToString("D3"));
                    stringArray[i] = input.GetNamedStringValue(fieldName);
                }

                output.SetNamedIntValue("numStrings", numStrings);

                for (int i = 0; i < numStrings; ++i)
                {
                    string fieldName = string.Format(CultureInfo.CurrentCulture, "output{0}", i.ToString("D3"));

                    output.SetNamedStringValue(fieldName, "Echo:" + stringArray[i]);
                }

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputNoOutput(RpcArchive input, RpcArchive output)
        {
            try
            {
                float inputParam1 = input.GetNamedFloatValue("param01");

                if (Math.Abs(inputParam1 - 3.14156f) > 0.0001f)
                {
                    return RpcError.InvalidArg;
                }

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutput(RpcArchive input, RpcArchive output)
        {
            try
            {
                int inputParam1 = input.GetNamedIntValue("param01");
                int inputParam2 = input.GetNamedIntValue("param02");

                if (inputParam1 != 211 || inputParam2 != 87)
                {
                    return RpcError.InvalidArg;
                }

                output.SetNamedIntValue("result", inputParam1 + inputParam2);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputIntCollection(RpcArchive input, RpcArchive output)
        {
            try
            {
                int[] inputParam = input.GetNamedIntCollection("param01");

                Array.Reverse(inputParam);

                output.SetNamedIntCollection("result", inputParam);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputInt64Collection(RpcArchive input, RpcArchive output)
        {
            try
            {
                long[] inputParam = input.GetNamedInt64Collection("param01");

                Array.Reverse(inputParam);

                output.SetNamedInt64Collection("result", inputParam);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputFloatCollection(RpcArchive input, RpcArchive output)
        {
            try
            {
                float[] inputParam = input.GetNamedFloatCollection("param01");

                Array.Reverse(inputParam);

                output.SetNamedFloatCollection("result", inputParam);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputStringCollection(RpcArchive input, RpcArchive output)
        {
            try
            {
                string[] inputParam = input.GetNamedStringCollection("stringCollection");

                Array.Reverse(inputParam);

                output.SetNamedStringCollection("result", inputParam);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputIntCollectionAndIntNumber(RpcArchive input, RpcArchive output)
        {
            try
            {
                int[] param1 = input.GetNamedIntCollection("param01");
                int param2 = input.GetNamedIntValue("param02");

                for (int i = 0; i < param1.Length; i++)
                {
                    param1[i] += param2;
                }

                output.SetNamedIntCollection("result", param1);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputMultipleCollections(RpcArchive input, RpcArchive output)
        {
            try
            {
                // Part 1 - which is a string collection
                string[] param1 = input.GetNamedStringCollection("param01");
                Array.Reverse(param1);
                output.SetNamedStringCollection("result1", param1);

                // Part 2 - which is an int collection
                int[] param2 = input.GetNamedIntCollection("param02");
                Array.Reverse(param2);
                output.SetNamedIntCollection("result2", param2);

                // Part 3 - which is a string collection
                string[] param3 = input.GetNamedStringCollection("param03");
                Array.Reverse(param3);
                output.SetNamedStringCollection("result3", param3);

                // Part 4 - which is a float collection
                float[] param4 = input.GetNamedFloatCollection("param04");
                Array.Reverse(param4);
                output.SetNamedFloatCollection("result4", param4);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputSubtraction(RpcArchive input, RpcArchive output)
        {
            try
            {
                short param1 = input.GetNamedShortValue("param01");
                short param2 = input.GetNamedShortValue("param02");

                if (param1 != 50 || param2 != 30)
                {
                    return RpcError.InvalidArg;
                }

                output.SetNamedIntValue("result", param1 - param2);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int NoInputNoOutput(RpcArchive input, RpcArchive output)
        {
            return 0;
        }

        private static int StopTheServer(RpcArchive input, RpcArchive output)
        {
            try
            {
                server2.Stop();
                output.SetNamedIntValue("result", 0);
                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int CallProcessPendingRequestsOnceOnServer3(RpcArchive input, RpcArchive output)
        {
            try
            {
                server3.ProcessPendingRequests();
                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int MultiInputMultiOutput(RpcArchive input, RpcArchive output)
        {
            try
            {
                byte param1 = input.GetNamedByteValue("param01");
                byte param2 = input.GetNamedByteValue("param02");
                double param3 = input.GetNamedDoubleValue("param03");

                if (param1 != 112 || param2 != 79 || param3 != 93.125)
                {
                    return RpcError.InvalidArg;
                }

                output.SetNamedIntValue("addOneAndTwo", param1 + param2);
                output.SetNamedDoubleValue("paramThreeSquared", param3 * param3);
                output.SetNamedIntValue("subtractTwoFromOne", param1 - param2);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int HookTimesOut(RpcArchive input, RpcArchive output)
        {
            Task.Delay(TimeSpan.FromSeconds(31)).Wait();

            return 0;
        }

        private static int HookWaitTenSeconds(RpcArchive input, RpcArchive output)
        {
            Task.Delay(TimeSpan.FromSeconds(10)).Wait();

            return 0;
        }

        private static int ReturnLargeNumberOfValues(RpcArchive input, RpcArchive output)
        {
            try
            {
                int numOutputValues = input.GetNamedIntValue("numOutputValues");

                for (int i = 0; i < numOutputValues; ++i)
                {
                    output.SetNamedIntValue(string.Format(CultureInfo.CurrentCulture, "out{0}", i.ToString("D3")), i);
                }

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int ReturnErrorCode(RpcArchive input, RpcArchive output)
        {
            return RpcError.UnknownFailure;
        }

        private static int SimultaneousHookCalls(RpcArchive input, RpcArchive output)
        {
            try
            {
                ushort param = input.GetNamedWordValue("param");

                if (param != 52 && param != 71)
                {
                    return RpcError.InvalidArg;
                }

                output.SetNamedIntValue("result", param * 100);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int LargeNumberOfInputValues(RpcArchive input, RpcArchive output)
        {
            try
            {
                int numParams = input.GetNamedIntValue("numParams");

                int sum = 0;

                for (int i = 0; i < numParams; ++i)
                {
                    string paramName = string.Format("in{0}", i.ToString("D3"));
                    int currentValue = input.GetNamedIntValue(paramName);

                    sum += currentValue;
                }

                output.SetNamedIntValue("result", sum);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int RetrieveMissingRpcArchiveField(RpcArchive input, RpcArchive output)
        {
            try
            {
                double notUsed = input.GetNamedDoubleValue("nonExistentField");
                return 0;
            }
            catch (Exception)
            {
                return RpcError.MakeHookError(0);
            }
        }

        private static int RpcArchiveValueIsNull(RpcArchive input, RpcArchive output)
        {
            return RpcError.MakeHookError(2);
        }

        private static int HookOrderIncrementNoOutput(RpcArchive input, RpcArchive output)
        {
            testHookOrderCounter++;
            return 0;
        }

        private static int GetHookOrderCounter(RpcArchive input, RpcArchive output)
        {
            try
            {
                output.SetNamedShortValue("result", testHookOrderCounter);
                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int AddArchiveToArchive(RpcArchive input, RpcArchive output)
        {
            try
            {
                // Get incoming RpcArchive object
                RpcArchive inputRpcArchive = input.GetNamedArchiveValue("param01");

                // Output RpcArchive object
                RpcArchive outRpcArchive = new RpcArchive();

                // Calling existing Hook Method
                int result = InputOutput(inputRpcArchive, outRpcArchive);

                if (result != 0)
                {
                    return result;
                }

                // Setting the output of Hook method
                output.SetNamedArchiveValue("resultLevel1", outRpcArchive);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int AddArchiveToArchiveWithIntColl(RpcArchive input, RpcArchive output)
        {
            try
            {
                // Getting incoming RpcArchive object
                RpcArchive inputRpcArchive = input.GetNamedArchiveValue("param01");

                // Output RpcArchive object
                RpcArchive outputRpcArchive = new RpcArchive();

                // Calling existing Hook method
                int result = InputOutputIntCollection(inputRpcArchive, outputRpcArchive);

                if (result != 0)
                {
                    return result;
                }

                // Setting the output of Hook method
                output.SetNamedArchiveValue("resultLevel1", outputRpcArchive);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int AddArchiveToArchiveWithStringColl(RpcArchive input, RpcArchive output)
        {
            try
            {
                // Get incoming RpcArchive object
                RpcArchive inputArchive1 = input.GetNamedArchiveValue("param01");

                // incoming RpcArchive object
                RpcArchive inputArchive2 = inputArchive1.GetNamedArchiveValue("param01");

                // Output RpcArchive object
                RpcArchive outputArchive1 = new RpcArchive();

                // Output RpcArchive object
                RpcArchive outputArchive2 = new RpcArchive();

                // Calling existing Hook method
                int result = InputOutputStringCollection(inputArchive2, outputArchive1);

                if (result != 0)
                {
                    return result;
                }

                // Setting the RpcArchive object to out RpcArchive object
                outputArchive2.SetNamedArchiveValue("resultLevel2", outputArchive1);

                // Setting the output of Hook method
                output.SetNamedArchiveValue("resultLevel1", outputArchive2);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int AddArchiveToArchiveWithInt64Coll(RpcArchive input, RpcArchive output)
        {
            try
            {
                // Get incoming RpcArchive object
                RpcArchive inputRpcArchive = input.GetNamedArchiveValue("param01");

                // Output RpcArchive object
                RpcArchive outputRpcArchive = new RpcArchive();

                // Calling existing Hook method
                int result = InputOutputInt64Collection(inputRpcArchive, outputRpcArchive);

                // Setting the output of Hook method
                output.SetNamedArchiveValue("resultLevel1", outputRpcArchive);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputArchiveCollectionToArchive(RpcArchive input, RpcArchive output)
        {
            try
            {
                const uint InOutArchivesCount = 4;

                RpcArchive[] inputParam = input.GetNamedArchiveCollection("param01");

                if (inputParam.Length != InOutArchivesCount)
                {
                    return RpcError.UnknownFailure;
                }

                RpcArchive[] outputParam = new RpcArchive[InOutArchivesCount];

                for (int i = 0; i < InOutArchivesCount; i++)
                {
                    outputParam[i] = new RpcArchive();
                }

                int inputParamInt = inputParam[0].GetNamedIntValue("param01");
                outputParam[0].SetNamedIntValue("result", inputParamInt + 1);

                long inputParamLong = inputParam[1].GetNamedInt64Value("param01");
                outputParam[1].SetNamedInt64Value("result", inputParamLong - 1);

                float inputParamFloat = inputParam[2].GetNamedFloatValue("param01");
                outputParam[2].SetNamedFloatValue("result", inputParamFloat * 3.0f);

                int result = EchoStrings(inputParam[3], outputParam[3]);

                if (result != 0)
                {
                    return result;
                }

                output.SetNamedArchiveCollection("resultLevel1", outputParam);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputArchiveCollectionToArchiveCollections(RpcArchive input, RpcArchive output)
        {
            try
            {
                const uint InOutArchivesCount = 4;

                RpcArchive[] inputParam = input.GetNamedArchiveCollection("param01");
                RpcArchive[] outputParam = new RpcArchive[InOutArchivesCount];

                for (int i = 0; i < InOutArchivesCount; i++)
                {
                    outputParam[i] = new RpcArchive();
                }

                // Setting output
                int result = InputOutputIntCollection(inputParam[0], outputParam[0]);

                if (result != 0)
                {
                    return result;
                }

                result = InputOutputInt64Collection(inputParam[1], outputParam[1]);

                if (result != 0)
                {
                    return result;
                }

                result = InputOutputFloatCollection(inputParam[2], outputParam[2]);

                if (result != 0)
                {
                    return result;
                }

                result = InputOutputStringCollection(inputParam[3], outputParam[3]);

                if (result != 0)
                {
                    return result;
                }

                output.SetNamedArchiveCollection("resultLevel1", outputParam);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputRpcArchiveToAnotherRpcArchiveForLoad(RpcArchive input, RpcArchive output)
        {
            try
            {
                RpcArchive rpcArchiveInput = input.GetNamedArchiveValue("rpcMultilevel");
                RpcArchive rpcArchiveOutput = new RpcArchive();

                int level = input.GetNamedIntValue("level");

                if (level > 0)
                {
                    for (int i = 1; i < level; i++)
                    {
                        // Getting the input RpcArchive object
                        RpcArchive rpcArchiveInputNode = rpcArchiveInput.GetNamedArchiveValue("rpcNode");
                        rpcArchiveInput = rpcArchiveInputNode;
                    }

                    int result = InputOutputMultipleCollectionsForRpcArchive(rpcArchiveInput, rpcArchiveOutput);

                    for (int i = 1; i < level; i++)
                    {
                        RpcArchive rpcArchiveOutputTemp = new RpcArchive();
                        rpcArchiveOutputTemp.SetNamedArchiveValue("rpcNodeResult", rpcArchiveOutput);
                        rpcArchiveOutput = rpcArchiveOutputTemp;
                    }

                    output.SetNamedArchiveValue("rpcMultilevelResult", rpcArchiveOutput);

                    return 0;
                }
                else
                {
                    return RpcError.UnknownFailure;
                }
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputRpcArchiveToAnotherRpcArchiveCollectionForLoad(RpcArchive input, RpcArchive output)
        {
            try
            {
                RpcArchive rpcArchiveInput = input.GetNamedArchiveValue("rpcMultilevel");
                RpcArchive rpcArchiveOutput = new RpcArchive();

                int level = input.GetNamedIntValue("level");

                if (level > 0)
                {
                    for (int i = 1; i < level; i++)
                    {
                        RpcArchive rpcArchiveInputNode = rpcArchiveInput.GetNamedArchiveValue("rpcNode");                            
                        rpcArchiveInput = rpcArchiveInputNode;
                    }

                    RpcArchive[] inputParam = rpcArchiveInput.GetNamedArchiveCollection("rpcCollection");
                    RpcArchive[] outputParam = new RpcArchive[inputParam.Length];

                    for (int i = 0; i < inputParam.Length; i++)
                    {
                        outputParam[i] = new RpcArchive();
                        int result = InputOutputMultipleCollectionsForRpcArchive(inputParam[i], outputParam[i]);

                        if (result != 0)
                        {
                            return result;
                        }
                    }         
   
                    rpcArchiveOutput.SetNamedArchiveCollection("rpcCollectionResult", outputParam);
            
                    for (int i = 1; i < level; i++)
                    {                              
                        RpcArchive rpcArchiveOutputTemp = new RpcArchive();
                        rpcArchiveOutputTemp.SetNamedArchiveValue("rpcNodeResult", rpcArchiveOutput);
                        rpcArchiveOutput = rpcArchiveOutputTemp;                       
                    }
            
                    output.SetNamedArchiveValue("rpcMultilevelResult", rpcArchiveOutput);

                    return 0;
                }
                else
                {
                    return RpcError.UnknownFailure;
                }
            }
            catch (Exception error)
            {
                return error.HResult;
            }
        }

        private static int InputOutputMultipleCollectionsForRpcArchive(RpcArchive input, RpcArchive output)
        {
            try
            {
                // Part 1 - which is a string collection
                string[] param1 = input.GetNamedStringCollection("param01");
                Array.Reverse(param1);
                output.SetNamedStringCollection("result1", param1);

                // Part 2 - which is a float collection
                float[] param2 = input.GetNamedFloatCollection("param02");
                Array.Reverse(param2);
                output.SetNamedFloatCollection("result2", param2);

                // Part 3 - which is an int collection
                int[] param3 = input.GetNamedIntCollection("param03");
                Array.Reverse(param3);
                output.SetNamedIntCollection("result3", param3);

                return 0;
            }
            catch (Exception error)
            {
                return error.HResult;
            }    
        }

        private static int ThrowException(RpcArchive input, RpcArchive output)
        {
            int hr = RpcError.UnknownFailure;

            int errorCode = input.GetNamedIntValue("errorCode");

            switch (errorCode)
            {
                case 600:
                    hr = RpcError.MakeHookError(600);
                    break;
                case 601:
                    hr = RpcError.MakeHookError(601);
                    break;
                case 602:
                    hr = RpcError.MakeHookError(602);
                    break;
                default:
                    hr = 0;
                    break;
            }

            return hr;
        }

        private static int AddBinaryDataToArchive(RpcArchive input, RpcArchive output)
        {
            RpcBinaryData blob = input.GetNamedBlobValue("param01");
            byte[] blobData = null;

            Windows.Security.Cryptography.CryptographicBuffer.CopyToByteArray(blob.Data, out blobData);

            Array.Reverse(blobData);

            output.SetNamedBlobValue(
                "param01",
                new RpcBinaryData(Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(blobData)));

            return 0;
        }

        private static int AddBinaryDataCollectionToArchive(RpcArchive input, RpcArchive output)
        {
            RpcBinaryData[] blobs = input.GetNamedBlobCollection("param01");

            foreach (RpcBinaryData blob in blobs)
            {
                byte[] blobData = null;
                Windows.Security.Cryptography.CryptographicBuffer.CopyToByteArray(blob.Data, out blobData);
                
                Array.Reverse(blobData);

                blob.Data = Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(blobData);
            }

            output.SetNamedBlobCollection("result", blobs);

            return 0;
        }

        private static int NoInputBinaryDataOutput(RpcArchive input, RpcArchive output)
        {
            const byte ArraySize = 5;

            byte[] data = new byte[(int)ArraySize];

            for (byte i = 0; i < ArraySize; i++)
            {
                data[i] = i;
            }

            output.SetNamedBlobValue(
                "result",
                new RpcBinaryData(Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(data)));

            return 0;
        }

        private static int EchoBinary(RpcArchive input, RpcArchive output)
        {
            output.SetNamedBlobValue("blob", input.GetNamedBlobValue("blob"));
            return 0;
        }

        private static int StartServerWithQueueSize(RpcArchive input, RpcArchive output)
        {
            serverWithQueueSize = new RpcServer(
                Guid.Parse("456F8F5C-0B90-465B-8D4E-D10CC8D690C0"),
                RpcServer.DefaultPort,
                input.GetNamedDwordValue("queueSize"));

            serverWithQueueSize.Start();

            return 0;
        }

        private static int StopServerWithQueueSize(RpcArchive input, RpcArchive output)
        {
            if (serverWithQueueSize != null)
            {
                serverWithQueueSize.Stop();
                serverWithQueueSize = null;
            }

            return 0;
        }

        private static int RunServerWithQueueSize(RpcArchive input, RpcArchive output)
        {
            if (serverWithQueueSize != null)
            {
                serverWithQueueSize.ProcessPendingRequests();
            }

            return 0;
        }
    }
}
